package main

import (
	_ "fmt"
	imperfectgo "github.com/imperfectgo/go-strftime"
	"github.com/pkg/errors"
	"os"
	"path"
	"time"
)

type TimeBasedRotator struct {
	timeDiffToUTC int64
	lastTime      int64
	period        int64
	dirname       string
	filename      string
	ofilename     string
	outFile       *os.File
	nowUnixNano   int64
	current       int64

	buf    [128]byte
	layout string
}

func (tw *TimeBasedRotator) Write(p []byte) (n int, err error) {

	err = tw.getFileHandler()
	if err != nil {
		return 0, errors.Wrap(err, `failed to acquite target io.Writer`)
	}

	return tw.outFile.Write(p)
}

func (tw *TimeBasedRotator) Writeln(p []byte) (n int, err error) {

	err = tw.getFileHandler()
	if err != nil {
		return 0, errors.Wrap(err, `failed to acquite target io.Writer`)
	}
	_, err = tw.outFile.Write(p)
	if err != nil {
		return 0, err
	}

	return tw.outFile.Write([]byte("\n"))
}

func (tw *TimeBasedRotator) WriteStrln(p string) (n int, err error) {

	err = tw.getFileHandler()
	if err != nil {
		return 0, errors.Wrap(err, `failed to acquite target io.Writer`)
	}

	return tw.outFile.WriteString(p + "\n")
}

func (tw *TimeBasedRotator) getFileHandler() error {
	tw.nowUnixNano = time.Now().UnixNano()
	tw.current = tw.nowUnixNano - ((tw.nowUnixNano + tw.timeDiffToUTC) % tw.period)
	if (tw.current - tw.lastTime) < tw.period {
		return nil
	}
	b := imperfectgo.AppendFormat(tw.buf[:0], time.Unix(0, tw.current), tw.layout)
	tw.ofilename = string(b)
	//fmt.Printf("FormatString filename=%s\n", filename)
	if tw.filename == tw.ofilename {
		return nil
	}
	//fmt.Printf("Rotate filename=%s %s\n", filename, path.Dir(filename))
	dirname := path.Dir(tw.ofilename)
	if tw.dirname != dirname {
		os.MkdirAll(path.Dir(tw.ofilename), os.ModePerm)
		tw.dirname = dirname
	}

	fh, err := os.OpenFile(tw.ofilename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
	if err != nil {
		return errors.Errorf("failed to open file %s: %s", tw.layout, err)
	}

	tw.outFile.Close()
	tw.outFile = fh
	tw.filename = tw.ofilename
	tw.lastTime = tw.current

	return nil
}

func (tw *TimeBasedRotator) Close() error {

	if tw.outFile != nil {
		tw.outFile.Close()
		tw.outFile = nil
	}
	return nil
}
